﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace M3G {


    public class Material : Object3D {
        #region Constant
        public const int  Ambient  = 1024;
        public const int  Diffuse  = 2048;
        public const int  Emissive = 4096;
        public const int  Specular = 8192;
        #endregion

        #region Field
        uint  ambient;
        uint  diffuse;
        uint  emissive;
        uint  specular;
        float shininess;
        bool  vertexColorTrackingEnabled;
        #endregion

        #region Method
        public Material () {
            this.ambient = 0x00333333u;
            this.diffuse = 0xffccccccu;
            this.emissive = 0x00000000u;
            this.specular = 0x00000000u;
            this.shininess = 0.0f;
            this.vertexColorTrackingEnabled = false;
        }

        protected Material (Material material) : base(material) {
            this.ambient = material.ambient;
            this.diffuse = material.diffuse;
            this.emissive = material.emissive;
            this.specular = material.specular;
            this.shininess = material.shininess;
            this.vertexColorTrackingEnabled = material.vertexColorTrackingEnabled;
        }

        public Dictionary<int, uint> Color {
            get {
                var dic = new Dictionary<int, uint> (4);
                dic.Add (Ambient, ambient);
                dic.Add (Diffuse, diffuse);
                dic.Add (Emissive, emissive);
                dic.Add (Specular, specular);
                return dic;
            }
        }

        public void SetColor (int target, uint color) {
            if (target < Ambient || target > Specular) {
                throw new ArgumentException ("Material must be Ambient,Diffuse,Emissive,Specular, mat=" + target);
            }

            if ((target & Ambient) != 0) {
                this.ambient = color  & 0x00ffffffu;
            }
            if ((target & Diffuse) != 0) {
                this.diffuse = color & 0xffffffffu;
            }
            if ((target & Emissive) != 0) {
                this.emissive = color & 0x00ffffffu;
            }
            if ((target & Specular) != 0) {
                this.specular = color & 0x00ffffffu;
            }
        }

        public float Shininess {
            get { return shininess; }
            set {
                var shininess = value;
                if (shininess < 0 || shininess > 128) {
                    throw new ArgumentException ("Specular shininess must be within [0,128], s=" + shininess);
                }

                this.shininess = shininess;
            }
        }

        public bool VertexColorTrackingEnabled {
            get { return vertexColorTrackingEnabled; }
            set { vertexColorTrackingEnabled = value; }
        }


        public override int Animate (int time) {

            base.Animate (time);

            var animatedAlpha          = new float[1];
            var animatedAmbientColor   = new float[3];
            var animatedDiffuseColor   = new float[4];
            var animatedEmissiveColor  = new float[3];
            var animatedShininess      = new float[1];
            var animatedSpecularColor  = new float[3];
            var value                  = new float[4];
            var animated               = new Dictionary<int, bool> (6);

            for (var i=0; i < AnimationTrackCount; i++) {
                var track = AnimationTrack[i];
                var ch    = AnimationTrackChannel[i];
                if (track.Controller == null || !track.Controller.Active (time)) {
                    continue;
                }
                var property = track.TargetProperty;
                var weight   = track.Controller.Weight;
                
                animated[property] = true;
                switch (property) {
                    case M3G.AnimationTrack.Alpha: {
                        track.Sample (time, ch, value);
                        animatedAlpha[0] = value[0] * weight;
                        break;
                    }
                    case M3G.AnimationTrack.AmbientColor: {
                        track.Sample (time, ch, value);
                        animatedAmbientColor[0] = value[0] * weight;
                        animatedAmbientColor[1] = value[1] * weight;
                        animatedAmbientColor[2] = value[2] * weight;
                        break;
                    }
                    case M3G.AnimationTrack.DiffuseColor: {
                        track.Sample (time, ch, value);
                        animatedDiffuseColor[0] = value[0] * weight;
                        animatedDiffuseColor[1] = value[1] * weight;
                        animatedDiffuseColor[2] = value[2] * weight;
                        animatedDiffuseColor[3] = value[3] * weight;
                        break;
                    }
                    case M3G.AnimationTrack.EmissiveColor: {
                        track.Sample (time, ch, value);
                        animatedEmissiveColor[0] = value[0] * weight;
                        animatedEmissiveColor[1] = value[1] * weight;
                        animatedEmissiveColor[2] = value[2] * weight;
                        break;
                    }
                    case M3G.AnimationTrack.Shininess: {
                        track.Sample (time, ch, value);
                        animatedShininess[0] = value[0] * weight;
                        break;
                    }
                    case M3G.AnimationTrack.SpecularColor: {
                        track.Sample (time, ch, value);
                        animatedSpecularColor[0] = value[0] * weight;
                        animatedSpecularColor[1] = value[1] * weight;
                        animatedSpecularColor[2] = value[2] * weight;
                        break;
                    }
                }// switch
            }  // for
            
            if (animated[M3G.AnimationTrack.Alpha]) {
                var a   = (uint)animatedAlpha[0].Clamp(0, 1) * 255;
                this.diffuse = (diffuse & 0x00ffffff) | (a << 24);
            }
            if (animated[M3G.AnimationTrack.AmbientColor]) {
                var r = (uint)animatedAmbientColor[0].Clamp (0, 1) * 255;
                var g = (uint)animatedAmbientColor[1].Clamp (0, 1) * 255;
                var b = (uint)animatedAmbientColor[2].Clamp (0, 1) * 255;
                this.ambient = (r << 16) | (g << 8) | (b << 0);
            }
            if (animated[M3G.AnimationTrack.DiffuseColor]) {
                var r = (uint)animatedDiffuseColor[0].Clamp (0, 1) * 255;
                var g = (uint)animatedDiffuseColor[1].Clamp (0, 1) * 255;
                var b = (uint)animatedDiffuseColor[2].Clamp (0, 1) * 255;
                this.diffuse = (diffuse & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
            }
            if (animated[M3G.AnimationTrack.EmissiveColor]) {
                var r = (uint)animatedEmissiveColor[0].Clamp (0, 1) * 255;
                var g = (uint)animatedEmissiveColor[1].Clamp (0, 1) * 255;
                var b = (uint)animatedEmissiveColor[2].Clamp (0, 1) * 255;
                this.emissive = (r << 16) | (g << 8) | (b << 0);
            }
            if (animated[M3G.AnimationTrack.Shininess]) {
                this.shininess = animatedShininess[0].Clamp (0, 128);
            }
            if (animated[M3G.AnimationTrack.SpecularColor]) {
                var r = (uint)animatedSpecularColor[0].Clamp (0, 1) * 255;
                var g = (uint)animatedSpecularColor[1].Clamp (0, 1) * 255;
                var b = (uint)animatedSpecularColor[2].Clamp (0, 1) * 255;
                this.specular = (r << 16) | (g << 8) | (b << 0);
            }

            return 0;

        }

        public override Object3D Duplicate () {
            return new Material (this);
        }
        #endregion



    }
}
